package com.exin.openfeign;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

import org.springframework.context.ApplicationContext;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

public class DefaultFeignReduceBuilder implements FeignReduceBuilder{
	/** The cache. */
	protected Map<Class<?>, Object> cache = new java.util.concurrent.ConcurrentHashMap<Class<?>, Object>();


	protected ClassLoader entityClassLoader;
	
	protected ApplicationContext applicationContext;
	

	
	public DefaultFeignReduceBuilder(ApplicationContext applicationContext) {
		super();
		this.applicationContext = applicationContext;
	}

	
	@Override
	@SuppressWarnings("unchecked")
	public <T> T getFeign(Class<T> mapperInterface) {
		if (cache.containsKey(mapperInterface)) {
			return (T) cache.get(mapperInterface);
		} else {
			T instance = this.buildInstance(mapperInterface);
			cache.put(mapperInterface, instance);
			return instance;
		}
	}
	
	public String getPath(Method method) {
		String[] paths = null;
		if(method.isAnnotationPresent(GetMapping.class)) {
			paths = method.getAnnotation(GetMapping.class).value();
		}else if(method.isAnnotationPresent(PostMapping.class)) {
			paths = method.getAnnotation(PostMapping.class).value();
		}else {
			paths = method.getAnnotation(RequestMapping.class).value();
		}
		if(paths == null || paths.length == 0) {
			return null;
		}
		String key = paths[0];
		return key;
	}

	
	@SuppressWarnings("unchecked")
	public <T> T buildInstance(Class<T> mapperInterface) {
		ClassLoader loader = null == entityClassLoader ? Thread.currentThread().getContextClassLoader() : entityClassLoader;
		FeignRequestMappingHandlerMapping mapping =  (FeignRequestMappingHandlerMapping)applicationContext.getBean(RequestMappingHandlerMapping.class);
		
		Method[] methods = ReflectionUtils.getDeclaredMethods(mapperInterface);
		Map<Method ,HandlerMethod> handlerMethodMap = new HashMap<>();
		
		for (Method method : methods) {
			String path = getPath(method);
			if(path!=null) {
				RequestMappingInfo feignMappinfo = mapping.get(method);
				HandlerMethod handlerMethod =  mapping.getHandlerMethods().get(feignMappinfo);
				if(handlerMethod == null) {
					throw new IllegalArgumentException("feign["+method.toString()+"]降级未找到对应controller 方法");
				}
				handlerMethodMap.put(method, handlerMethod);
				mapping.unregisterMapping(feignMappinfo);
			}
		}
		Object target =  Proxy.newProxyInstance(loader==null?this.getClass().getClassLoader():loader, new Class<?>[] { mapperInterface },
                new FeignReduceProxy(mapperInterface , handlerMethodMap , this.applicationContext));
		return (T)target;
	}


}
